/* Moderator	: helpbygrace ( helpbygrace_AT_gmail.com ) */

#include <stdio.h>
#include <stdlib.h>

#include "mem.h"
#include "common.h"

/* mem_t의 포인터를 모두 NULL로 초기화시킨다.
   기본적으로 limit을 1024로 두어, 총 메모리가 4메가를 넘지
   못하게 만든다. 그러나 이것이 전체를 0번지부터 4메가까지를
   말하는 것은 아니고, 4기가 내 어느 영역이든지 만들어질 수
   있다.*/
void init_mem(mem_t* mem, word limit)
{
  int i = 0;
  while(i < BLOCKS)
    {
      mem->b[i]	= NULL;
      i++;
    }
  mem->capacity	= limit;
  mem->limit	= limit;
}

void fini_mem(mem_t* mem)
{
  int i = mem->limit - 1;
  while(i >= 0)
    {
      if( mem->b[i] != NULL )
	{
	  free(mem->b[i]);
	}
      i--;
    }
}

int
op_mem (proc_t		*proc,
	mem_t		*mem,
	word		addr,
	mem_mode_t	mode,
	mem_data_t	data,
	word		st,
	word		*ld	)
{
  word	tag	= MTAG(addr);
  word	idx	= MIDX(addr);
  word	offset	= MOFFSET(addr);

  if (mem->b[tag] == NULL)
    {
      if (mem->capacity == 0)
	return NOT_ENOUGH_MEMORY;
      /* memory가 allocated가 아니면 새로 block을 만들어서 할당한다. */
      mem->b[tag]		= (mem_block_t *)malloc(sizeof(mem_block_t));
      if(mem->b[tag] == NULL)
	return NOT_ENOUGH_MEMORY;
      mem->b[tag]->offset	= tag;
      mem->capacity--;
    }

  /* 얼라인 된 주소인지 검사 */
  switch (data)
    {
    case mem_data_ub:
    case mem_data_sb:
      /* 어떤 주소도 접근가능 */
      break;
    case mem_data_uh:
    case mem_data_sh:
      if ( 0 != (offset & 0x1))
	return MISALIGNED_MEMORY;
      break;
    case mem_data_w:
      if ( 0 != offset )
	return MISALIGNED_MEMORY;
      break;
    }

  switch (mode)
    {
    case mem_mode_ld:
      switch (data)
	{
	case mem_data_ub:
	  *ld	= MDATA(tag,idx);
	  break;
	case mem_data_sb:
	  *ld	= SEXT(MDATA(tag,idx),8);
	  break;
	case mem_data_uh:
	  *ld	= MDATA(tag,idx)
	    | (MDATA(tag,idx+1)<<8);
	  break;
	case mem_data_sh:
	  *ld	= SEXT(MDATA(tag,idx)
		       | (MDATA(tag,idx+1) << 8),16);
	  break;
	case mem_data_w:
	  *ld	= MDATA(tag,idx)
	    | (MDATA(tag,idx+1)<<8)
	    | (MDATA(tag,idx+2)<<16)
	    | (MDATA(tag,idx+3)<<24);
	  break;
	}

      break;
    case mem_mode_st:
      switch (data)
	{
	case mem_data_ub:
	case mem_data_sb:
	  MDATA(tag,idx)	= st & 0xFF;
	  break;
	case mem_data_uh:
	case mem_data_sh:
	  MDATA(tag,idx)	= st & 0xFF;
	  MDATA(tag,idx+1)	= (st >> 8) & 0xFF;
	  break;
	case mem_data_w:
	  MDATA(tag,idx)	= st & 0xFF;
	  MDATA(tag,idx+1)	= (st >> 8) & 0xFF;
	  MDATA(tag,idx+2)	= (st >> 16) & 0xFF;
	  MDATA(tag,idx+3)	= (st >> 24) & 0xFF;
	  break;
	}
      break;
    }
  /* memory count update */
  proc->c_mem++;
  return SUCCESS;
}
